package edu.ntu.cloudroid.server.backendservice;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.google.appengine.api.datastore.Key;

import edu.ntu.cloudroid.model.IDs;
import edu.ntu.cloudroid.model.PMF;
import edu.ntu.cloudroid.model.data.AbstractData;
import edu.ntu.cloudroid.model.data.LocationData;
import edu.ntu.cloudroid.model.policy.AbstractPrivacyPreference;
import edu.ntu.cloudroid.model.profile.ProfileData;
import edu.ntu.cloudroid.nongui.util.TimeCounter;
import edu.ntu.cloudroid.server.RequestServlet;
import edu.ntu.cloudroid.server.UploadServlet;

/**
 * @author ttadinh
 * This is a helper class that provide methods
 * for adding and retrieving generic Abstract object from the 
 * data store
 */
public class DataService {
	
	static TimeCounter counter = new TimeCounter("data insert",
			UploadServlet.logger);
	/**
	 * Simply adding the AbstractData to the datastore
	 */
	public static void addData(AbstractData data){
		counter.start(true);
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try{			
			pm.currentTransaction().begin();			
			pm.makePersistent(data);
			pm.currentTransaction().commit();			
		}
		catch (Exception e){
			e.printStackTrace();
		}
		pm.close();
		counter.end(true);
	}
	
	public static AbstractData getLatestData(ProfileData person, int dataId)
			throws Exception {
		PersistenceManager pm = PMF.get().getPersistenceManager();		
		try {
			Query locQuery = pm.newQuery(getAbstractDataClass(dataId));
			locQuery.declareParameters("String personKey");
			locQuery.setFilter("name == personKey");
			locQuery.setOrdering("timestamp DESC");

			List<AbstractData> locs = (List<AbstractData>) locQuery
					.execute(person.getEmailAddress());
			return pm.detachCopy(locs.get(0));
		} finally {
			pm.close();
		}
	}
	
	//Get all data from the last timestamp
	//This is the default option when there's no obligation
	public static List<AbstractData> getData(ProfileData person, Date lastTs, int dataId) throws Exception{
		List<AbstractData> cachedList = DataCache.getFromCache(person.getEmailAddress(), lastTs);
		if (cachedList!=null)
			return cachedList;
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		/*pm.getFetchPlan().addGroup(AbstractData.FETCH_GROUP_NAME);
		pm.getFetchPlan().setMaxFetchDepth(2);*/	
		List<AbstractData> returnList = new ArrayList<AbstractData>();
		try {
			Query query = pm.newQuery(getAbstractDataClass(dataId), "name == personParam");// && timestamp >= timestampParam");
			
			//query.setFilter("profileKey == personParam && timestamp > timestampParam)");
			
			query.declareParameters("String personParam");//, " + Date.class.getName() + " timestampParam");
			//query.setOrdering("timestamp asc");
			
			@SuppressWarnings("unchecked")
			List<AbstractData> result = (List<AbstractData>) query.execute(person.getEmailAddress());//, lastTs);
			for (AbstractData v : result){
				
				if (v.getTimestamp().after(lastTs)) {
					returnList.add(pm.detachCopy(v));
				}
			}			
			//query.execute();
			RequestServlet.logger.info("n data = "+result.size()+", data id = "+dataId);
			DataCache.updateCache(person.getEmailAddress(), lastTs, returnList);
			return returnList;
		} finally {
			pm.close();		
		}	
		
	}
	
	public static List<AbstractData> getAllData(ProfileData person, int dataId) throws Exception{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		/*pm.getFetchPlan().addGroup(AbstractData.FETCH_GROUP_NAME);
		pm.getFetchPlan().setMaxFetchDepth(2);*/
		List<AbstractData> returnList = new ArrayList<AbstractData>();
		
		try {
			Query query = pm.newQuery(getAbstractDataClass(dataId));
			
			query.setFilter("profileKey == personParam");
			
			query.declareParameters(Key.class.getName() + " personParam");							
			@SuppressWarnings("unchecked")
			List<AbstractData> result = (List<AbstractData>) query.execute(
					person.getKey());
			
			for (AbstractData v : result){
				returnList.add(pm.detachCopy(v));
				
			}			
			//query.execute();
			
			return returnList;
		} finally {
			pm.close();		
		}	
	}
	
	public static void removeDataFromUser(int id, String user) throws Exception{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		/*pm.getFetchPlan().addGroup(AbstractData.FETCH_GROUP_NAME);
		pm.getFetchPlan().setMaxFetchDepth(2);*/	
		List<AbstractData> returnList = new ArrayList<AbstractData>();
		long start = System.currentTimeMillis();
		try {
			Query query = pm.newQuery(getAbstractDataClass(id), "name == person");
			query.declareParameters("String person");
			query.getFetchPlan().setFetchSize(10000);
			@SuppressWarnings("unchecked")
			List<AbstractData> result = (List<AbstractData>) query.execute(user);
			RequestServlet.logger.info("deleting user = "+user+" size = "+result.size());
			for (AbstractData v : result){
				pm.deletePersistent(v);
			}			
			
			long end = System.currentTimeMillis();
			RequestServlet.logger.info("data delete time = "+(end-start)+" for "+result.size());
		} finally {
			pm.close();		
		}
	}
	public static void deleteAllData(int id) throws Exception{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		/*pm.getFetchPlan().addGroup(AbstractData.FETCH_GROUP_NAME);
		pm.getFetchPlan().setMaxFetchDepth(2);*/	
		List<AbstractData> returnList = new ArrayList<AbstractData>();
		long start = System.currentTimeMillis();
		try {
			Query query = pm.newQuery(getAbstractDataClass(id));
			query.getFetchPlan().setFetchSize(10000);
			RequestServlet.logger.info("data class is "+getAbstractDataClass(id).getCanonicalName()+" for id = "+id);
			@SuppressWarnings("unchecked")
			List<AbstractData> result = (List<AbstractData>) query.execute();
			RequestServlet.logger.info("deleting " +result.size());
			for (AbstractData v : result){
				pm.deletePersistent(v);
			}			
			
			long end = System.currentTimeMillis();
			RequestServlet.logger.info("data delete time = "+(end-start)+" for "+result.size());
		} finally {
			pm.close();		
		}
	}
	
	public static Class getAbstractDataClass(int id) throws Exception{
		return Class.forName("edu.ntu.cloudroid.model.data."
				+ IDs.DataID.values()[id].name());
	}
	
	public static Object getAbstractDataObject(int id) throws Exception{
		Class cl = Class.forName("edu.ntu.cloudroid.model.data."
				+ IDs.DataID.values()[id].name());
		return cl.newInstance();
	}
}

